﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Security.Cryptography;
using System.Runtime.InteropServices;


namespace 文件同步工具
{
    public partial class Form1 : Form
    {
        public Form1()//窗口初始化入口
        {
            InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false;//允许线程中操作UI
        }

        private void Form1_Load(object sender, EventArgs e)//启动窗口创建完毕
        {
            初始化文件对比模式();
            输出日志_("已启动");
            添加待同步文件信息到表格("文件同步", "xxxxx文件路径", "xxxx文件名称", "处理成功");
            添加待同步文件信息到表格("文件同步1", "xxxxx文件路径1", "xxxx文件名称1", "处理成功1");
        }
        int 文件对比模式 = 0;
        int 日志计次a = 0;
        bool 停止请求 = false;
        int 计数文件已查 = 0;
        int 计数文件差异 = 0;
        int 计数文件已删 = 0;
        int 计数文件夹已查 = 0;
        int 计数文件夹已删 = 0;


        private void 初始化文件对比模式()
        {
            comboBox_文件对比.Items.Clear();
            comboBox_文件对比.Items.Add("快速模式【只对比文件大小】");
            comboBox_文件对比.Items.Add("正常模式【对比文件MD5】");
            if (comboBox_文件对比.Items.Count < 1) return;
            comboBox_文件对比.SelectedIndex = 0;
        }

        private void textBox_待同步1_DoubleClick(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();
            dialog.Description = "请选择待同步文件夹目录";
            string pathA = "";
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                if (string.IsNullOrEmpty(dialog.SelectedPath)) return;
                pathA = dialog.SelectedPath;
                if (pathA.Substring(pathA.Length - 1) != "\\") pathA = pathA + "\\";
                textBox_待同步1.Text = pathA;
                textBox_日志.Focus();//获得焦点
            }
        }

        private void textBox_待同步2_DoubleClick(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();
            dialog.Description = "请选择保存到的文件夹";
            if (dialog.ShowDialog() != DialogResult.OK) return;//如果用户未选择文件夹则返回
            string pathA = dialog.SelectedPath;
            if (string.IsNullOrWhiteSpace(pathA)) return;//如果文件夹目录为空则返回
            if (pathA.Substring(pathA.Length - 1) != "\\") pathA = pathA + "\\";//给文件夹路径加上斜杠\
            textBox_待同步2.Text = pathA;
            textBox_日志.Focus();
        }

        public bool 文件对比校验(string 待对比文件1,string 待对比文件2)
        {
            if (!File.Exists(待对比文件2)) return false;
            int[] size = new int[2];
            FileInfo fileInfo = new FileInfo(待对比文件1);
            size[0] = (int)fileInfo.Length;
            fileInfo = new FileInfo(待对比文件2);
            size[1] = (int)fileInfo.Length;
            if (size[0] != size[1]) return false;
            if (文件对比模式 == 0) return true;
            string[] md5 = new string[2];
            md5[0] = 文件取MD5(待对比文件1);
            md5[1] = 文件取MD5(待对比文件2);
            if (md5[0] != md5[1]) return false;
            return true;
        }
        public string 文件取MD5(string filepath)
        {
            FileStream file = new FileStream(filepath, FileMode.Open);
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] retval = md5.ComputeHash(file);
            file.Close();
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < retval.Length; i++)
            {
                sb.Append(retval[i].ToString("x2"));
            }
            return sb.ToString();
        }
        public void 输出状态_(string msg)
        {
            toolStripStatusLabel2.Text = msg;
        }
        public void 输出日志_(string msg)
        {
            //int 日志计次a = 0;
            日志计次a = 日志计次a + 1;
            if (日志计次a >= 200)
            {
                string data = textBox_日志.Text;
                string[] text = data.Split(new string[] { "\r\n" }, StringSplitOptions.None);
                int length = text.Length;
                日志计次a = 0;
                data = "";
                for(int b = length - 10; b< length; b++)
                {
                    日志计次a = 日志计次a + 1;
                    data = data + text[b]+"\r\n";
                }
                textBox_日志.Text = data;
            }
            textBox_日志.AppendText("["+DateTime.Now.ToString() + "] " + msg + "\r\n");
        }
        public void 添加待同步文件信息到表格(string type,string pathName,string fileName,string msg)
        {
            //listView1.Items.Clear();//清空表格
            int sid = listView1.Items.Count + 1;
            ListViewItem item = new ListViewItem(sid.ToString());
            item.SubItems.Add(type);
            item.SubItems.Add(pathName);
            item.SubItems.Add(fileName);
            item.SubItems.Add(msg);
            listView1.BeginUpdate();//停止绘表
            listView1.Items.Add(item);
            listView1.EndUpdate();//继续绘表
        }
        public void 扫描同步子线程_文件(string 待备份目录,string 保存目录)
        {
            bool logic;
            string msg = "";
            string[] pathArr = Directory.GetFiles(待备份目录, "*.*", SearchOption.TopDirectoryOnly);
            //对比是否变动
            for(int a = 0; a < pathArr.Length; a++)
            {
                if (停止请求) return;
                pathArr[a] = Path.GetFileName(pathArr[a]);
                计数文件已查 = 计数文件已查 + 1;
                logic = 文件对比校验(待备份目录 + pathArr[a], 保存目录 + pathArr[a]);
                if (logic) continue;//如果是一样则到下一个循环

                计数文件差异 = 计数文件差异 + 1;
                logic = true;
                try { File.Copy(待备份目录 + pathArr[a], 保存目录 + pathArr[a], true);}
                catch (Exception){logic = false;}
                msg = "同步文件" + (logic ? "ok" : "失败") + "," + 待备份目录 + pathArr[a];
                输出日志_(msg);
                添加待同步文件信息到表格("同步文件", 待备份目录, pathArr[a], logic ? "ok" : "失败");
            }
            //反向对比是否存在，不存在则删除
            pathArr = Directory.GetFiles(保存目录, "*.*", SearchOption.TopDirectoryOnly);
            for (int a = 0; a < pathArr.Length; a++)
            {
                if (停止请求) return;
                pathArr[a] = Path.GetFileName(pathArr[a]);
                if (File.Exists(待备份目录 + pathArr[a])) continue;//存在则跳过

                计数文件已删 = 计数文件已删 + 1;
                logic = true;
                try { File.Delete(保存目录 + pathArr[a]); }
                catch (Exception) { logic = false; }
                msg = "删除文件" + (logic ? "ok" : "失败") + "," + 保存目录 + pathArr[a];
                输出日志_(msg);
                添加待同步文件信息到表格("删除文件", 保存目录, pathArr[a], logic ? "ok" : "失败");
            }
        }
        public void 扫描同步子线程_文件夹(string 待备份目录, string 保存目录)
        {
            扫描同步子线程_文件(待备份目录, 保存目录);
            //------------对比文件夹
            string msg = "";
            bool logic = false;
            string[] pathArr = Directory.GetDirectories(待备份目录, "*", SearchOption.TopDirectoryOnly);
            for (int a = 0; a < pathArr.Length; a++)
            {
                pathArr[a] = pathArr[a].Substring(pathArr[a].LastIndexOf("\\") + 1);//取出文件夹名称
                if (停止请求) return;
                msg = "文件夹:" + 计数文件夹已查.ToString() + "/" + 计数文件夹已删.ToString();
                msg = msg + "文件:" + 计数文件已查.ToString() + "/" + 计数文件已删.ToString() + "/" + 计数文件差异.ToString();
                msg = msg + 待备份目录 + pathArr[a];
                输出状态_(msg);
                计数文件夹已查 = 计数文件夹已查 + 1;
                logic = Directory.Exists(保存目录 + pathArr[a]);
                if (!logic)
                {//文件夹不存在则进行创建
                    Directory.CreateDirectory(保存目录 + pathArr[a]);
                }
                扫描同步子线程_文件夹(待备份目录 + pathArr[a] + "\\", 保存目录 + pathArr[a] + "\\");
            }
            //-----------反向对比，删除不存在的文件夹
            pathArr = Directory.GetDirectories(保存目录, "*", SearchOption.TopDirectoryOnly);
            for (int a = 0; a < pathArr.Length; a++)
            {
                pathArr[a] = pathArr[a].Substring(pathArr[a].LastIndexOf("\\") + 1);//取出文件夹名称
                if (停止请求) return;
                logic = Directory.Exists(待备份目录 + pathArr[a]);
                if (logic) continue;//如果存在则到下一循环
                计数文件夹已删 = 计数文件夹已删 + 1;
                logic = true;
                try { Directory.Delete(保存目录 + pathArr[a], true); }
                catch (Exception) { logic = false; }
                msg = "删除文件夹" + (logic ? "ok" : "失败") + "，" + 保存目录 + pathArr[a];
                输出日志_(msg);
                添加待同步文件信息到表格("删除文件夹", 保存目录, pathArr[a], logic ? "ok" : "失败");
            }
        }

        private void button_扫描_Click(object sender, EventArgs e)
        {
            ///扫描同步子线程_文件(@"C:\Users\linyi\Desktop\other", "");
            //扫描同步子线程_文件夹(@"C:\Users\linyi\Desktop\other", "");
            Task task = new Task(扫描主控);
            task.Start();
        }
        public void 扫描主控()
        {
            文件对比模式 = comboBox_文件对比.SelectedIndex;
            string msg = "";
            string 待备份目录 = textBox_待同步1.Text.Trim();
            string 保存目录 = textBox_待同步2.Text.Trim();
            if (待备份目录 == "")
            {
                MessageBox.Show("请选择要备份的目录");return;
            }
            if (保存目录 == "")
            {
                MessageBox.Show("请选择要保存到的目录");return;
            }
            if(Directory.Exists(待备份目录)==false || Directory.Exists(保存目录) == false)
            {
                MessageBox.Show("待备份目录 或者 保存目录 错误不存在，请重新设置正确目录！");return;
            }
            if (待备份目录.Substring(待备份目录.Length - 1) != "\\")
            {
                待备份目录 = 待备份目录 + "\\";
            }
            if (保存目录.Substring(保存目录.Length - 1) != "\\")
            {
                保存目录 = 保存目录 + "\\";
            }
            button_扫描.Enabled = false;
            //------------------------------------
            long time = GetTickCount64();
            停止请求 = false;
            计数文件已查 = 0;
            计数文件差异 = 0;
            计数文件已删 = 0;
            计数文件夹已查 = 0;
            计数文件夹已删 = 0;
            listView1.Items.Clear();//清空表格

            扫描同步子线程_文件夹(待备份目录, 保存目录);
            //--------------------
            msg = "结束扫描，文件夹，总" + 计数文件夹已查.ToString() + "/已删" + 计数文件夹已删.ToString();
            msg = msg + "，文件：总" + 计数文件已查.ToString() + "/已删" + 计数文件已删.ToString() + "/同步" + 计数文件差异.ToString();
            输出状态_(msg);
            time = (GetTickCount64() - time) / 1000;
            输出日志_("完成全部扫描，耗时：" + time.ToString() + "秒！");
            //--------------------------------
            button_扫描.Enabled = true;
        }

        private void button_停止_Click(object sender, EventArgs e)
        {
            停止请求 = true;
        }

        [DllImport("kernel32.dll")]
        public static extern long GetTickCount64();
    }

    class listViewNF : System.Windows.Forms.ListView
    {
        public listViewNF()
        {
            //开启双缓冲
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.EnableNotifyMessage, true);
        }
        protected override void OnNotifyMessage(Message m)
        {
            if (m.Msg != 0x14)
            {
                base.OnNotifyMessage(m);
            }
        }
    }
}
